/*
 * Copyright 2019 The Netty Project
 *
 * The Netty Project licenses this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */
package io.netty.example.fep.netty.client;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.EventLoop;
import io.netty.example.fep.netty.common.FepProtocolMsg;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.ReferenceCountUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.TimeUnit;

/**
 * Description：
 * netty 客户端 处理器
 *
 * @author : chenjie03@imdada.cn
 * Date: 2020/5/26
 * Time: 13:50
 */
public class ClientMainHandler extends ChannelInboundHandlerAdapter {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     * NettyClient
     */
    private NettyClient client;

    /**
     * 自定义handler处理器
     */
    private ClientHandler handler;

    public ClientMainHandler(NettyClient client, ClientHandler handler) {
        this.client = client;
        this.handler = handler;
    }

    /**
     * 接收服务端消息
     *
     * @param ctx channel上下文
     * @param msg 消息
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        FepProtocolMsg message = (FepProtocolMsg) msg;
        logger.info("client recived  => {}", message);
        if (message == null) {
            return;
        }
        try {
            switch (message.getType()) {
                case FepProtocolMsg.Constants.TYPE_HEART_BEAT:
                    //客户端收到服务端心跳响应
                    handler.activeHandler(message);
                    break;
                case FepProtocolMsg.Constants.TYPE_QUERY:
                    //远程查询
                    handler.queryHandler(message);
                    break;
                default:
                    //其他监控
                    handler.otherHandler(message);
                    break;
            }
        } catch (Exception e) {
            logger.error("server handler error:", e);
        } finally {
            //msg引用减1，主动释放buf
            ReferenceCountUtil.release(msg);
        }
    }

    /**
     * 首次建立连接
     *
     * @param ctx channel上下文
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        FepProtocolMsg connectMsg = new FepProtocolMsg(FepProtocolMsg.Constants.TYPE_HEART_BEAT, client.getClientId());
        ctx.writeAndFlush(connectMsg);
        logger.info("client => server : 握手");
    }


    /**
     * 空闲发送心跳
     *
     * @param ctx channel上下文
     * @param evt 事件
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent) {
            FepProtocolMsg heartBeat = new FepProtocolMsg(FepProtocolMsg.Constants.TYPE_HEART_BEAT, client.getClientId());
            ctx.writeAndFlush(heartBeat);
            logger.info("client => server : 心跳");
        } else {
            super.userEventTriggered(ctx, evt);
        }
    }

    /**
     * 异常处理
     *
     * @param ctx   channel上下文
     * @param cause 异常
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        logger.error("client => server : 异常=", cause);
        ctx.close();
    }

    /**
     * 断线重连（5秒不间断重试）
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        final EventLoop eventLoop = ctx.channel().eventLoop();
        //5秒重新建立连接
        eventLoop.schedule(new Runnable() {
            @Override
            public void run() {
                try {
                    client.connect();
                } catch (Exception e) {
                    logger.error("client => server : 断线重连，异常=", e);
                }
            }
        }, 5L, TimeUnit.SECONDS);
        super.channelInactive(ctx);
    }
}
